package com.zjun.demo.experimentaldemo.indexbar

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.text.TextPaint
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes
import com.zjun.demo.experimentaldemo.R
import kotlin.math.roundToInt

/**
 * 一个索引的View
 */
class IndexBarView : View {

    var onIndexListener: OnIndexPressedListener? = null

    /**
     * 文字宽度
     */
    private val mTextWidths = mutableListOf<Float>()

    /**
     * 文字高度
     */
    private val mTextHeights = mutableListOf<Float>()

    /**
     * 绘制文字的画笔
     */
    private val mTextPaint = TextPaint()

    /**
     * 文字的高度
     */
    private var mTextHeight: Float = 0.0f

    /**
     * 文字的宽度
     */
    private var mTextWidth: Float = 0.0f

    /**
     * 初始选中的索引,对应当前选择的是哪一个索引
     */
    private var mIndex: Int = -1


    /**
     * 索引之间的间隙
     */
    private var mIndexInterval: Float = 0.0f

    /**
     * 索引数据
     */
    private var mIndexData = arrayOf(
        "A", "B", "C", "D", "E", "F", "G", "H", "I",
        "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
        "W", "X", "Y", "Z"
    )

    /**
     * 索引文字所在的区域
     */
    private var mIndexPointFs = mutableListOf<RectF>()

    /**
     * 选中的样式
     */
    private var mSelectedDrawable: Drawable? = null

    /**
     * 选中的样式
     */
    private var mTextSize: Float = 0.0f

    /**
     * 选中的样式
     */
    private var mTextColor: Int = Color.BLACK

    /**
     * 没有选中的样式
     */
    private var mNormalDrawable: Drawable? = null

    constructor(context: Context) : super(context) {
        initialization()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initialization(attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initialization(attrs)
    }

    private fun initialization(attrs: AttributeSet? = null) {
        context.withStyledAttributes(attrs, R.styleable.IndexBarView) {

            val selectedDrawableId = getResourceId(R.styleable.IndexBarView_ib_selected, -1)
            if (selectedDrawableId != -1) {
                mSelectedDrawable = ContextCompat.getDrawable(context, selectedDrawableId)
            }

            val normalDrawableId = getResourceId(R.styleable.IndexBarView_ib_normal, -1)
            if (normalDrawableId != -1) {
                mNormalDrawable = ContextCompat.getDrawable(context, normalDrawableId)
            }

            mTextSize = getDimension(R.styleable.IndexBarView_android_textSize, sp2px(14f))
            mTextColor = getColor(R.styleable.IndexBarView_android_textColor, Color.BLACK)

            mIndexInterval = getDimension(R.styleable.IndexBarView_ib_index_interval, dip2px(4f))

        }
        mTextPaint.run {
            isAntiAlias = true
            textSize = mTextSize
            color = mTextColor
        }

        repeat(mIndexData.count()) {
            mIndexPointFs.add(RectF())
        }
    }

    /**
     * 更新索引数据
     */
    fun updateIndexData(indexData: Array<String>, index: Int = -1) {
        mIndexData = indexData
        mIndex = index
        mIndexPointFs.clear()
        repeat(mIndexData.count()) {
            mIndexPointFs.add(RectF())
        }
        requestLayout()
    }

    /**
     * 更新索引下标数据
     */
    fun updateIndex(index: Int) {
        if (index in mIndexData.indices) {
            mIndex = index
            invalidate()
        }
    }

    /**
     * 更新索引下标数据
     */
    fun updateIndex(tag: String) {
        val index = mIndexData.indexOfFirst { tag == it }
        if(index!=-1 && index!=mIndex){
            mIndex = index
            invalidate()
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var textWidth = 0.0f
        var textHeight = 0.0f
        mTextWidths.clear()
        mTextHeights.clear()
        mIndexData.forEachIndexed { index, indexData ->
            val measureTextWidth = mTextPaint.measureText(indexData)
            val fontMetrics = mTextPaint.fontMetrics
            val measureTextHeight = fontMetrics.descent - fontMetrics.ascent
            if (textWidth < measureTextWidth) {
                textWidth = measureTextWidth
            }
            if (textHeight < measureTextHeight) {
                textHeight = measureTextHeight
            }
            mTextWidths.add(measureTextWidth)
            mTextHeights.add(measureTextHeight)
        }

        mTextHeight = textHeight
        mTextWidth = textWidth

        val needWidth = paddingStart + paddingEnd + textWidth
        val needHeight = paddingTop + paddingBottom + textHeight * mIndexData.size + mIndexInterval * mIndexData.size

        var newBottom = 0f
        mIndexData.forEachIndexed { index, _ ->
            val left = 0f
            var top = 0f
            var bottom = 0f
            if (index > 0 && index < mIndexData.size - 1) {
                top = newBottom
                bottom = textHeight + mIndexInterval + top
            } else if (mIndexData.size == 1) {
                top = paddingTop.toFloat()
                bottom = textHeight + top + mIndexInterval
            } else if (index == 0) {
                top = paddingTop.toFloat()
                bottom = textHeight + mIndexInterval + top
            } else if (index == mIndexData.lastIndex) {
                top = newBottom
                bottom = textHeight + mIndexInterval + top
            }
            newBottom = bottom
            mIndexPointFs[index].set(left, top, needWidth, bottom)
            Log.i("TAG", "onMeasure: ${mIndexPointFs[index].height()}")
        }
        setMeasuredDimension(
            MeasureSpec.makeMeasureSpec(needWidth.toInt(), MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(needHeight.toInt(), MeasureSpec.EXACTLY)
        )
    }

    /**
     * 绘制文字
     */
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawSelectedDrawable(canvas)
        drawNormalDrawable(canvas)
        drawText(canvas)
    }

    /**
     * 绘制选中背景
     */
    private fun drawSelectedDrawable(canvas: Canvas) {
        val radius = mTextWidth.coerceAtMost(mTextHeight) + dip2px(1.5f)
        mSelectedDrawable?.run {
            if (mIndex in 0 until mIndexPointFs.size) {
                val height: Int = mIndexPointFs[mIndex].height().roundToInt()
                val centerX = (paddingStart + ((mTextWidth - mTextWidths[mIndex]) / 2) + mTextWidths[mIndex] / 2).roundToInt()
                val centerY = mIndexPointFs[mIndex].top + height / 2f
                setBounds(
                    (centerX - radius).toInt(),
                    (centerY - radius).toInt(),
                    (centerX + radius).toInt(),
                    (centerY + radius).toInt()
                )
                draw(canvas)
            }
        }
    }

    /**
     * 绘制未选中背景
     */
    private fun drawNormalDrawable(canvas: Canvas) {
        mNormalDrawable?.run {
            mIndexPointFs.forEachIndexed { index, element ->
                if (index != mIndex) {
                    val width: Int = (element.right - element.left).roundToInt()
                    val height: Int = (element.bottom - element.top).roundToInt()
                    setBounds(
                        element.left.toInt(),
                        element.top.toInt(),
                        width,
                        height
                    )
                    draw(canvas)
                }
            }
        }
    }

    /**
     * 绘制文字
     */
    private fun drawText(canvas: Canvas) {
        mIndexPointFs.forEachIndexed { index, element ->
            mTextPaint.color = if (mIndex == index) Color.WHITE else mTextColor
            val y = element.top + element.height() / 2 + mTextHeights[index] / 2 - mTextPaint.fontMetrics.descent
            canvas.drawText(mIndexData[index], paddingStart + ((mTextWidth - mTextWidths[index]) / 2), y, mTextPaint)
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {

            MotionEvent.ACTION_DOWN,
            MotionEvent.ACTION_MOVE,
            -> {
                mIndexPointFs.forEachIndexed { index, rectF ->
                    if (event.y > rectF.top && event.y < rectF.bottom) {
                        if (mIndex != index) {
                            mIndex = index
                            invalidate()
                            onIndexListener?.onIndexPressed(mIndex, mIndexData[mIndex])
                            return true
                        }
                    }
                }
            }

            MotionEvent.ACTION_UP -> {
                onIndexListener?.onMotionEventEnd()
            }

        }
        return true
    }

    /**
     * dp转px
     * @param dipValue dp值
     */
    private fun dip2px(dipValue: Float): Float {
        val scale: Float = context.resources.displayMetrics.density
        return dipValue * scale + 0.5f
    }

    /**
     * 将sp值转换为px值，保证文字大小不变
     * DisplayMetrics类中属性scaledDensity
     */
    private fun sp2px(spValue: Float): Float {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_SP,
            spValue,
            context.resources.displayMetrics
        )
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        onIndexListener = null
    }

    interface OnIndexPressedListener {
        fun onIndexPressed(index: Int, text: String)

        fun onMotionEventEnd()
    }
}